using System;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Data;
using System.Web.Security;

namespace MembershipAdapter
{
    public class MembershipProviderAdapter : MembershipProvider
    {
        private IUserRepository adaptee;
        private MembershipProviderSettings settings;

        public IUserRepository Adaptee
        {
            get
            {
                if (this.adaptee == null)
                {
                    throw new NoNullAllowedException("MembershipProviderAdapter: adaptee property should be injected.");
                }

                return this.adaptee;
            }

            set { this.adaptee = value; }
        }

        public override string ApplicationName
        {
            get { return this.settings.AppName; }
            set { this.settings.AppName = value; }
        }

        public override bool EnablePasswordReset
        {
            get { return this.settings.EnablePasswordReset; }
        }

        public override bool EnablePasswordRetrieval
        {
            get { return this.settings.EnablePasswordRetrieval; }
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return this.settings.MaxInvalidPasswordAttempts; }
        }

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return this.settings.MinRequiredNonalphanumericCharacters; }
        }

        public override int MinRequiredPasswordLength
        {
            get { return this.settings.MinRequiredPasswordLength; }
        }

        public override int PasswordAttemptWindow
        {
            get { return this.settings.PasswordAttemptWindow; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { return this.settings.PasswordFormat; }
        }

        public override string PasswordStrengthRegularExpression
        {
            get { return this.settings.PasswordStrengthRegularExpression; }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { return this.settings.RequiresQuestionAndAnswer; }
        }

        public override bool RequiresUniqueEmail
        {
            get { return this.settings.RequiresUniqueEmail; }
        }

        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            var result = this.Adaptee.ChangePassword(username, oldPassword, newPassword);
            return result;
        }

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            var result = this.Adaptee.ChangePasswordQuestionAndAnswer(username
                , password
                , newPasswordQuestion
                , newPasswordAnswer);
            return result;
        }

        public override MembershipUser CreateUser(string username
            , string password
            , string email
            , string passwordQuestion
            , string passwordAnswer
            , bool isApproved
            , object providerUserKey
            , out MembershipCreateStatus status)
        {
            var args = new ValidatePasswordEventArgs(username, password, true);
            this.OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (this.RequiresUniqueEmail && this.GetUserNameByEmail(email) != string.Empty)
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            var user = this.GetUser(username, true);
            if (user == null)
            {
                this.Adaptee.CreateUser(username
                    , password
                    , email
                    , passwordQuestion
                    , passwordAnswer
                    , isApproved);

                user = this.GetUser(username, true);
                status = user != null ? MembershipCreateStatus.Success : MembershipCreateStatus.DuplicateUserName;
                return user;
            }

            status = MembershipCreateStatus.ProviderError;
            return user;
        }

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            this.Adaptee.DeleteUser(username);
            return true;
        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            var result = this.Adaptee.FindUsersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords);
            return result;
        }

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            var result = this.Adaptee.FindUsersByName(usernameToMatch, pageIndex, pageSize, out totalRecords);
            return result;
        }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            var result = this.Adaptee.GetAllUsers(pageIndex, pageSize, out totalRecords);
            return result;
        }

        public override int GetNumberOfUsersOnline()
        {
            var result = this.Adaptee.GetNumberOfUsersOnline();
            return result;
        }

        public override string GetPassword(string username, string answer)
        {
            var result = this.Adaptee.GetPassword(username, answer);
            return result;
        }

        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            var result = this.Adaptee.GetMembershipUser(username, userIsOnline);
            return result;
        }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            var result = this.Adaptee.GetMembershipUser(providerUserKey, userIsOnline);
            return result;
        }

        public override string GetUserNameByEmail(string email)
        {
            var result = this.Adaptee.GetUserNameByEmail(email);
            return result;
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            // NB: this method is internal!!! HttpRuntime.CheckAspNetHostingPermission(AspNetHostingPermissionLevel.Low, SR.Feature_not_supported_at_this_level);
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (string.IsNullOrEmpty(name))
            {
                name = "MembershipProviderAdapter";
            }

            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", SR.GetString(SR.MembershipProviderAdapter_description));
            }

            base.Initialize(name, config);
            var userRepositoryType = string.Empty;
            SecUtility.GetAndRemoveStringAttribute(config, "userRepositoryType", this.GetType().Name, ref userRepositoryType);
            this.adaptee = this.InitializeAdaptee(userRepositoryType, config);
        }

        public override string ResetPassword(string username, string answer)
        {
            var result = this.Adaptee.ResetPassword(username, answer);
            return result;
        }

        public override bool UnlockUser(string userName)
        {
            var result = this.Adaptee.UnlockUser(userName);
            return result;
        }

        public override void UpdateUser(MembershipUser user)
        {
            this.Adaptee.UpdateUser(user);
        }

        public override bool ValidateUser(string username, string password)
        {
            var result = this.Adaptee.ValidateUser(username, password);
            return result;
        }

        private IUserRepository InitializeAdaptee(string userRepository, NameValueCollection config)
        {
            if (string.IsNullOrWhiteSpace(userRepository))
            {
                throw new ProviderException(SR.GetString(SR.Missing_UserRepository_type));
            }

            var type = Type.GetType(userRepository);
            var result = (IUserRepository)Activator.CreateInstance(type, new object[] { config });
            this.settings = result.Settings;
            return result;
        }
    }
}